package org.ala.tcp_feign_client.spring;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.ala.tcp_feign_client.context.TcpFeignClientContext;
import org.ala.tcp_feign_client.spring.annotations.EnableTcpFeignClients;
import org.ala.tcp_feign_common.config.TcpFeignConfigure;
import org.ala.tcp_feign_common.logs.LoggerFactory;
import org.ala.tcp_feign_common.spring.annotations.TcpFeignClient;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AbstractClassTestingTypeFilter;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;


/**
 * TcpFeignClient代理注册
 *
 * @author 骆毅(ala)
 * @date 2024年4月21日
 */
public class TcpFeignClientProxyRegisterPostProcessor implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

	
	static Logger log = LoggerFactory.getLogger("tcp-feign", "client");
	
	
	/**
	 * spring环境变量
	 */
	protected Environment environment;
	/**
	 * 资源加载器
	 */
	protected ResourceLoader resourceLoader;
	/**
	 * 配置文件
	 */
	protected TcpFeignConfigure configure;
	
	
	@Override
	public void setEnvironment(Environment environment) {
		this.environment = environment;
		
		//	加载配置文件
		configure = TcpFeignConfigure.build(environment);
		TcpFeignClientContext.instance().init(configure);
	}
	@Override
	public void setResourceLoader(ResourceLoader resourceLoader) {
		this.resourceLoader = resourceLoader;
	}
	
	
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
			BeanNameGenerator importBeanNameGenerator) {
		//	扫描TcpFeignClient接口，注册代理
		scannerIterator(importingClassMetadata, (beanDefinition) -> {
			if (beanDefinition instanceof AnnotatedBeanDefinition) {
				AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition;
				registerTcpFeignClientProxy(annotatedBeanDefinition, registry);
			}
		});
	}
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
	}
	
	
	/**
	 * 注册代理
	 */
	protected void registerTcpFeignClientProxy(AnnotatedBeanDefinition annotatedBeanDefinition, BeanDefinitionRegistry registry) {
		AnnotationMetadata annotationMetadata = annotatedBeanDefinition.getMetadata();
		//	只作用于接口类型
		Assert.isTrue(annotationMetadata.isInterface(), "TcpFeignClient注解只能作用于接口类型 class:" + annotationMetadata.getClassName());
		
		//	取必要的参数
		Map<String, Object> attrs = annotationMetadata.getAnnotationAttributes(TcpFeignClient.class.getCanonicalName());
		//	bean名称
		String beanName = beanName(attrs, annotatedBeanDefinition.getBeanClassName());
		//	远程服务名称
		String remoteName = getAttr(attrs, "", "value");
		if (StringUtils.isEmpty(remoteName)) {remoteName = getAttr(attrs, "", "name");}
		//	远程服务host，port
		String hosts = getAttr(attrs, "", "hosts");
		Assert.isTrue(!StringUtils.isEmpty(remoteName) || !StringUtils.isEmpty(hosts), "value|name 或者 hosts 不能同时为空 class:" + annotationMetadata.getClassName());
		//	根url
		String path = getAttr(attrs, "", "path");
		//	是否专线
		boolean dedicated = getAttr(attrs, false, "idDedicated");
		
		
		BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(TcpFeignFactoryBean.class);
		builder.addPropertyValue("beanName", beanName);
		builder.addPropertyValue("remoteName", remoteName);
		builder.addPropertyValue("hosts", hosts);
		builder.addPropertyValue("path", path);
		builder.addPropertyValue("dedicated", dedicated);
		builder.addPropertyValue("configure", configure);
		try {
			builder.addPropertyValue("type", ClassUtils.forName(annotationMetadata.getClassName(), TcpFeignClientProxyRegisterPostProcessor.class.getClassLoader()));
		} catch (ClassNotFoundException | LinkageError e) {
			throw new RuntimeException(e);
		}
		builder.setPrimary(true);
		builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
		
		//	注册beanDefined
		AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
		beanDefinition.setPrimary(true);
		
		BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, beanName);
		BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
	}
	/**
	 * beanName
	 */
	protected String beanName(Map<String, Object> attrs, String className) {
		String beanName = className;
		//	取value
		String value = (String)attrs.get("contextId");
		if (!StringUtils.isEmpty(value)) {beanName = value;}
		return beanName;
	}
	/**
	 * 取远程服务名称
	 */
	@SuppressWarnings("unchecked")
	protected <T> T getAttr(Map<String, Object> attrs, T defaultValue, String key) {
		Object value = null;
		value = attrs.getOrDefault(key, defaultValue);
		if (value instanceof String) {value = environmentValue(value.toString());}
		return (T)value;
	}
	
	
	static Pattern p = Pattern.compile("\\$\\{([\\w|\\d|_|-]\\.?)+\\}");
	/**
	 * 变量替换
	 * <p>	替换value中${}部分。若env中找不到值，原样返回
	 */
	protected String environmentValue(String value) {
		//	找到所有的变量符
		Matcher m = p.matcher(value);
		Map<String, String> tags = new HashMap<>();
		while (m.find()) {
			String t = m.group();
			t = t.substring(2, t.length()-1);
			
			String v = environment.getProperty(t);
			if (!StringUtils.isEmpty(t)) {
				tags.put(t, v);
			}
		}
		for (Entry<String, String> kv : tags.entrySet()) {
			value = value.replaceAll("\\$\\{" + kv.getKey() + "\\}", kv.getValue());
		}
		
		return value;
	}
	
	
	/**
	 * 获取class扫描器，并初始化扫描路径
	 */
	protected void scannerIterator(AnnotationMetadata metadata, Consumer<BeanDefinition> consumer) {
		ClassPathScanningCandidateComponentProvider scanner = createScanner();
		scanner.setEnvironment(environment);
		scanner.setResourceLoader(resourceLoader);
		
		//	EnableTcpFeignClient注解中配置的参数
		Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableTcpFeignClients.class.getCanonicalName());
		//	要扫描的包目录
		Set<String> basePackages = null;
		//	DelayMessageClient注解过滤器
		AnnotationTypeFilter annotationFilter = new AnnotationTypeFilter(TcpFeignClient.class);
		
		//	取EnableDelayMessageClients注解中的clients
		Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("classes");
		
		//	如果clients不为空，则basePackages为clients那几个class的包目录
		//	并且追加内部类判断（clients中允许包含内部类）
		if (clients != null && clients.length > 0) {
			basePackages = new HashSet<>();
			final Set<String> clientClasses = new HashSet<>();
			for (Class<?> client : clients) {
				basePackages.add(ClassUtils.getPackageName(client));
				clientClasses.add(client.getCanonicalName());
			}
			//	给scanner注册annotation过滤器和内部类过滤器
			AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
				@Override
				protected boolean match(ClassMetadata metadata) {
					//	内部类的$转换为.
					String className = metadata.getClassName().replaceAll("\\$", ".");
					return clientClasses.contains(className);
				}
			};
			scanner.addIncludeFilter(new MultiTypeFilter(new TypeFilter[] {filter, annotationFilter}));
		}
		//	如果为空，则从EnableDelayMessageClients的注解中取basePackages
		//	默认会给成启动类所在的package
		else {
			basePackages = basePackages(attrs, metadata);
			scanner.addIncludeFilter(annotationFilter);
		}
		
		//	扫描每一个BeanDefined
		for (String pkg : basePackages) {
			Set<BeanDefinition> beanDefines = scanner.findCandidateComponents(pkg);
			for (BeanDefinition bd : beanDefines) {
				try {
					consumer.accept(bd);
				} catch (Exception e) {
					log.error("[TcpFeignClientProxyRegisterPostProcessor scannerIterator] 构建TcpFeignClient代理BeanDefinition异常. " + e.getMessage(), e);
				}
			}
		}
	}
	/**
	 * 根据EnableDelayMessageClients的注解取basePackages
	 */
	protected Set<String> basePackages(Map<String, Object> attrs, AnnotationMetadata metadata) {
		Set<String> basePackages = new HashSet<>();
		//	检测value
		for (String pkg : (String[]) attrs.get("values")) {
			if (StringUtils.hasText(pkg)) basePackages.add(pkg);
		}
		//	检测basePackages
		for (String pkg : (String[]) attrs.get("packages")) {
			if (StringUtils.hasText(pkg)) basePackages.add(pkg);
		}
		//	如果basePackages还为空，则追加启动类的package
		if (basePackages.isEmpty()) {
			basePackages.add(ClassUtils.getPackageName(metadata.getClassName()));
		}
		return basePackages;
	}
	/**
	 * 初始化扫描器
	 */
	protected ClassPathScanningCandidateComponentProvider createScanner() {
		return new ClassPathScanningCandidateComponentProvider() {
			@Override
			protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
				if (beanDefinition.getMetadata().isIndependent()) {
					if (
							//	如果类定义是接口
							beanDefinition.getMetadata().isInterface()
							//	并且只实现1接口
							&& beanDefinition.getMetadata().getInterfaceNames().length == 1
							//	并且是注解类型
							&& Annotation.class.getName().equals(beanDefinition.getMetadata().getInterfaceNames()[0])
							) {
						try {
							Class<?> target = ClassUtils.forName(beanDefinition.getMetadata().getClassName(), TcpFeignClientProxyRegisterPostProcessor.class.getClassLoader());
							return !target.isAnnotation();
						} catch (ClassNotFoundException | LinkageError e) {
							this.logger.warn("[TcpFeignClientProxyRegisterPostProcessor createScanner] 加载类型错误:" + beanDefinition.getMetadata().getClassName(), e);
						}
					}
					return true;
				}
				return false;
			}
		};
	}
	
	
	/**
	 * 多filter
	 */
	protected static class MultiTypeFilter implements TypeFilter {
		protected TypeFilter[] filters;
		public MultiTypeFilter(TypeFilter[] filters) {
			super();
			this.filters = filters;
		}
		@Override
		public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
			for (TypeFilter filter : filters) {
				//	只要有1个条件不满足则判定为false
				if (!filter.match(metadataReader, metadataReaderFactory)) return false;
			}
			return true;
		}
	}

}
